package org.example.ymcc.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.example.ymcc.constant.CacheConstants;
import org.example.ymcc.constant.MQConstants;
import org.example.ymcc.constant.RConst;
import org.example.ymcc.domain.KillActivity;
import org.example.ymcc.domain.KillCourse;
import org.example.ymcc.dto.PreOrderDto;
import org.example.ymcc.exception.GlobalException;
import org.example.ymcc.mapper.KillActivityMapper;
import org.example.ymcc.mapper.KillCourseMapper;
import org.example.ymcc.result.JsonResult;
import org.example.ymcc.ro.KillRo;
import org.example.ymcc.service.IKillCourseService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.ymcc.util.AssertUtil;
import org.example.ymcc.util.CodeGenerateUtils;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zcj
 * @since 2024-06-24
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {
    @Autowired
    private KillActivityMapper killActivityMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Override
    public boolean save(KillCourse entity) {
        entity.setCreateTime(new Date());
        KillActivity killActivity = killActivityMapper.selectById(entity.getActivityId());
        entity.setStartTime(killActivity.getBeginTime());
        entity.setEndTime(killActivity.getEndTime());
        entity.setTimeStr(killActivity.getTimeStr());
        return super.save(entity);
    }

    @Override
    public void publishKillCourse2Redis() {
        //发布秒杀课程到redis里面
        //什么样是要发布的    临近
        Date date = new Date();
        LambdaQueryWrapper<KillCourse> wrapper = new LambdaQueryWrapper<KillCourse>();
        wrapper.eq(KillCourse::getPublishStatus, RConst.Num.INT_0);
//        wrapper.le(KillCourse::getStartTime, date);
//        wrapper.ge(KillCourse::getEndTime, date);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(date);
        wrapper.like(KillCourse::getStartTime,format);
        List<KillCourse> list = super.list(wrapper);
       //放入都redis里面去

        Map<String,KillCourse> courseMap = new HashMap<String,KillCourse>();
        list.forEach(killCourse -> {
            //得到库存   放入库存到redis里面
            redisTemplate.opsForValue().set(CacheConstants.KILL_STORE+":"+killCourse.getId(),killCourse.getKillCount());
            //我们是不是要将秒杀的课程展示给用户看
            courseMap.put(killCourse.getId().toString(),killCourse);
            //更新数据库
            killCourse.setPublishStatus(RConst.Num.INT_1);
            killCourse.setPublishTime(date);
            super.updateById(killCourse);
        });

        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(CacheConstants.KEY_KILL_COURSE);
        hashOps.putAll(courseMap);

    }

    @Override
    public JsonResult killList() {
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(CacheConstants.KEY_KILL_COURSE);
        return JsonResult.success().setData(operations.values());
    }

    @Override
    public JsonResult killCourseDetail(Long killId) {
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(CacheConstants.KEY_KILL_COURSE);
        return JsonResult.success().setData(operations.get(killId.toString()));
    }

    @Override
    public JsonResult kill(KillRo killRo) {
        Date currentTime = new Date();
        //1. 秒杀判断：是否登录，秒杀码是否合法，秒杀时间是否合法，用户是否已经秒杀过，秒杀数量限制
        Long courseKillId = killRo.getCourseKillId();
        //秒杀码是否合法
        Object o = redisTemplate.boundHashOps(CacheConstants.KEY_KILL_COURSE).get(courseKillId.toString());
        AssertUtil.isNotNull(o,"秒杀不合法！");
        //用户是否已经秒杀过当前课程
        AssertUtil.isTrue(!redisTemplate.hasKey(CacheConstants.KILL_RECORD+"_"+killRo.getLoginId()+"_"+killRo.getCourseKillId()), "请勿重复秒杀同一课程！");
        //秒杀时间是否合法
        KillCourse killCourse = (KillCourse)o;
        AssertUtil.isTrue(currentTime.after(killCourse.getStartTime()) && currentTime.before(killCourse.getEndTime()), "秒杀活动时间不合法！");

        //秒杀数量限制
        AssertUtil.isTrue(killRo.getNum() <= killCourse.getKillLimit(), "每人限购"+killCourse.getKillLimit()+"份！");
        //2. Redisson分布式锁：信号量减库存  库存预热的key
        RSemaphore semaphore = redissonClient.getSemaphore(CacheConstants.KILL_STORE + ":" + killRo.getCourseKillId());
        //分布式锁：尝试抢锁（尝试扣减库存，减本次秒杀的数量）
        String orderNo = "";
        if(semaphore.tryAcquire(killRo.getNum())){
            //3. 创建订单号，准备“预创订单数据”
            orderNo = CodeGenerateUtils.generateOrderSn(killRo.getLoginId());

            //4. 保存“预创订单数据到Redis”   PreOrderDto：需要什么字段？依据是下订单需要哪些字段 就设置哪些字段
            PreOrderDto preOrderDto = new PreOrderDto();
            preOrderDto.setOrderNo(orderNo);
            preOrderDto.setUserId(killRo.getLoginId());
            preOrderDto.setTitle("秒杀订单");
            preOrderDto.setOrderType(1);
            preOrderDto.setPrice(killCourse.getKillPrice());
            preOrderDto.setCount(killRo.getNum());
            preOrderDto.setCourseId(killCourse.getCourseId());
            preOrderDto.setCourseName(killCourse.getCourseName());
            preOrderDto.setCoursePic(killCourse.getCoursePic());
            preOrderDto.setKillCourseId(killCourse.getId());
            redisTemplate.opsForValue().set(CacheConstants.KEY_PREFIX_PRE_ORDER+":"+orderNo, preOrderDto);

            //5. 发送延迟消息到MQ，用作下单超时处理（5分钟）：【消费者写在订单服务中】5分钟后用户仍未下单（通过订单编号去查询订单表）
            // ，则取消资格【通过订单编号为key删除redis中保存的预创订单】
//            rocketMQTemplate.syncSend( MQConstants.TOPIC_ORDER_TIMEOUT+":"+MQConstants.TAGS_ORDER_TIMEOUT,
//                    MessageBuilder.withPayload(orderNo).build(),
//                    3000, 3);

            //6. 保存用户秒杀记录到Redis防止重复秒杀  KILL_RECORD_用户id_秒杀课程id =》 value随意
            redisTemplate.opsForValue().set(CacheConstants.KILL_RECORD+"_"+killRo.getLoginId()+"_"+killCourse.getId(), 1);
        }else {
            throw new GlobalException("秒杀失败！");
        }
        //7. 返回订单号给前端，用作下单依据
        return JsonResult.success().setData(orderNo);
    }
}
